# -*- coding: utf-8 -*-

# tests/test_build_manpages.py
# Part of ‘manpage’, a Python library for making Unix manual documents.
#
# Copyright © 2015–2016 Ben Finney <ben+python@benfinney.id.au>
#
# This is free software: see the grant of license at end of this file.

""" Unit tests for Distutils build system helpers. """

import sys
import os
import os.path
import argparse
import collections
import distutils.dist
import unittest
import unittest.mock

__package__ = os.path.basename(os.path.dirname(os.path.abspath(__file__)))
__import__(__package__)
sys.path.insert(1, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import manpage.build_manpages
import manpage.document
import manpage.argparse_help
from .test_document import (
        setup_metadata_instance,
        )
from .test_maker import (
        setup_maker_instance,
        )
from .test_argparse_help import (
        setup_parser_instance,
        )


class assemble_maker_TestCase(unittest.TestCase):
    """ Test cases for function `assemble_maker`. """

    default_test_args = {
            'maker_class': manpage.document.ManPageMaker,
            'name': "lorem",
            'whatis': "ipsum",
            }

    scenarios = [
            ('simple', {
                'expected_class': default_test_args['maker_class'],
                'expected_metadata': manpage.document.MetaData(
                    name=default_test_args['name'],
                    whatis=default_test_args['whatis'],
                    manual=None, section=None, source=None),
                }),
            ('command-manpage', {
                'test_args_extra': {
                    'maker_class': manpage.argparse_help.CommandManPageMaker,
                    'parser': object(),
                    },
                'expected_class': manpage.argparse_help.CommandManPageMaker,
                'expected_metadata': manpage.document.MetaData(
                    name=default_test_args['name'],
                    whatis=default_test_args['whatis'],
                    manual=None, section=None, source=None),
                }),
            ]

    def setUp(self):
        """ Set up test fixtures. """
        super().setUp()

        for (scenario_name, scenario) in self.scenarios:
            scenario['test_args'] = self.default_test_args.copy()
            if 'test_args_extra' in scenario:
                scenario['test_args'].update(scenario['test_args_extra'])

        func_patcher = unittest.mock.patch.object(
                manpage.argparse_help.CommandManPageMaker, 'set_parser')
        func_patcher.start()
        self.addCleanup(func_patcher.stop)

    def test_returns_instance_of_specified_class(self):
        """ Should return an instance of specified class. """
        for (scenario_name, scenario) in self.scenarios:
            with self.subTest(scenario=scenario_name):
                result = manpage.build_manpages.assemble_maker(
                        **scenario['test_args'])
                self.assertIsInstance(result, scenario['expected_class'])

    def test_result_has_specified_metadata(self):
        """ Should return an instance with specified `metadata`. """
        for (scenario_name, scenario) in self.scenarios:
            with self.subTest(scenario=scenario_name):
                result = manpage.build_manpages.assemble_maker(
                        **scenario['test_args'])
                self.assertEqual(
                        result.metadata, scenario['expected_metadata'])

    def test_sets_specified_parser(self):
        """ Should set command line parser with specified value. """
        for (scenario_name, scenario) in self.scenarios:
            with self.subTest(scenario=scenario_name):
                if 'parser' not in scenario['test_args']:
                    self.skipTest("no parser specified")
                result = manpage.build_manpages.assemble_maker(
                        **scenario['test_args'])
                expected_parser = scenario['test_args']['parser']
                method = manpage.argparse_help.CommandManPageMaker.set_parser
                method.assert_called_with(expected_parser)


def setup_distribution_instance(testcase):
    """ Set up the `Distribution` instance for the test case. """
    distribution = unittest.mock.MagicMock()
    distribution.set_spec(distutils.dist.Distribution)
    distribution.get_name.return_value = "lorem-ipsum"
    distribution.get_version.return_value = "dolor.sit.amet"

    testcase.test_distribution_instance = distribution


class DocumentBuilder_BaseTestCase(unittest.TestCase):
    """ Base class for test cases for class `DocumentBuilder`. """

    builder_class = manpage.build_manpages.DocumentBuilder
    maker_class = manpage.argparse_help.CommandManPageMaker
    parser_class = argparse.ArgumentParser

    def setUp(self):
        """ Set up test fixtures. """
        setup_parser_instance(
                self, test_args={'prog': "lorem"}, test_actions=[])
        setup_maker_instance(self)
        self.test_maker_instance.set_parser(self.test_parser_instance)
        setup_distribution_instance(self)

        self.test_args = {
                'maker': self.test_maker_instance,
                'distribution': self.test_distribution_instance,
                }

        self.test_instance = self.builder_class(**self.test_args)


class DocumentBuilder_TestCase(DocumentBuilder_BaseTestCase):
    """ Test cases for class `DocumentBuilder`. """

    def test_has_specified_maker(self):
        """ Should have specified `maker` value. """
        expected_maker = self.test_maker_instance
        self.assertIs(self.test_instance.maker, expected_maker)

    def test_has_specified_distribution(self):
        """ Should have specified `distribution` value. """
        expected_distribution = self.test_distribution_instance
        self.assertIs(self.test_instance.distribution, expected_distribution)


class DocumentBuilder_make_source_field_TestCase(
        DocumentBuilder_BaseTestCase):
    """ Test cases for method `DocumentBuilder.make_source_field`. """

    def test_returns_expected_text(self):
        """ Should return expected text for distribution metadata. """
        distribution = self.test_distribution_instance
        result = self.test_instance.make_source_field()
        expected_result = "{name} {version}".format(
                name=distribution.get_name.return_value,
                version=distribution.get_version.return_value)
        self.assertEqual(result, expected_result)


class DocumentBuilder_insert_distribution_section_TestCase(
        DocumentBuilder_BaseTestCase):
    """ Test cases for method `insert_distribution_section`. """

    scenarios = [
            ('simple', {
                'content_sections': collections.OrderedDict(
                    (title, None)
                    for title in [
                        "NAME", "SYNOPSIS", "DESCRIPTION",
                        "SEE ALSO"]),
                'test_section_content': "Lorem ipsum",
                'expected_section_titles': [
                    "NAME", "SYNOPSIS", "DESCRIPTION",
                    "DISTRIBUTION",
                    "SEE ALSO"],
                }),
            ('sections-seealso-missing', {
                'content_sections': collections.OrderedDict(
                    (title, None)
                    for title in [
                        "NAME", "SYNOPSIS", "DESCRIPTION"]),
                'test_section_content': "Lorem ipsum",
                'expected_error': ValueError,
                }),
            ('sections-nonstandard', {
                'content_sections': collections.OrderedDict(
                    (title, None)
                    for title in [
                        "NULLAM", "VEHICULA", "VEL LACUS", "IN TINCIDUNT"]),
                'test_section_content': "Lorem ipsum",
                'expected_error': ValueError,
                }),
            ]

    def test_sections_in_expected_sequence(self):
        """ Should result in expected sequence of section. """
        distribution = self.test_distribution_instance
        for (scenario_name, scenario) in self.scenarios:
            if 'expected_error' in scenario:
                self.skipTest("call expected to fail in this scenario")
            with self.subTest(scenario=scenario_name):
                document = self.test_maker_instance.make_manpage()
                document.content_sections = scenario['content_sections']
                self.test_instance.insert_distribution_section(document)
                section_titles = list(document.content_sections.keys())
                self.assertEqual(
                        section_titles, scenario['expected_section_titles'])

    def test_raises_expected_error_when_no_seealso_section(self):
        """ Should raise error when “SEE ALSO” section missing. """
        distribution = self.test_distribution_instance
        for (scenario_name, scenario) in self.scenarios:
            if 'expected_error' not in scenario:
                self.skipTest("no expected error in this scenario")
            with self.subTest(scenario=scenario_name):
                document = self.test_maker_instance.make_manpage()
                document.content_sections = scenario['content_sections']
                with self.assertRaises(scenario['expected_error']):
                    self.test_instance.insert_distribution_section(document)


class DocumentBuilder_build_TestCase(DocumentBuilder_BaseTestCase):
    """ Test cases for method `build`. """

    scenarios = [
            ('simple', {
                'expected_class': manpage.document.CommandDocument,
                'expected_source': "lorem-ipsum dolor.sit.amet",
                }),
            ('source-specified', {
                'metadata': manpage.document.MetaData(
                    "lorem-ipsum", "donec ultrices ut nisl a placerat",
                    None, None, "Donec non luctus libero"),
                'expected_class': manpage.document.CommandDocument,
                'expected_source': "Donec non luctus libero",
                }),
            ('source-unspecified', {
                'metadata': manpage.document.MetaData(
                    "lorem-ipsum", "donec ultrices ut nisl a placerat",
                    None, None, None),
                'expected_class': manpage.document.CommandDocument,
                'expected_source': "lorem-ipsum dolor.sit.amet",
                }),
            ]

    def test_document_is_of_expected_class(self):
        """ Should return an instance of expected class. """
        for (scenario_name, scenario) in self.scenarios:
            with self.subTest(scenario=scenario_name):
                result = self.test_instance.build()
                self.assertIsInstance(result, scenario['expected_class'])

    def test_document_has_expected_source_field(self):
        """ Should return an instance of expected class. """
        for (scenario_name, scenario) in self.scenarios:
            with self.subTest(scenario=scenario_name):
                if 'metadata' in scenario:
                    self.test_maker_instance.metadata = scenario['metadata']
                    self.test_instance = self.builder_class(**self.test_args)
                result = self.test_instance.build()
                self.assertIsInstance(result, scenario['expected_class'])


# This is free software: you may copy, modify, and/or distribute this work
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; version 3 of that license or any later version.
#
# No warranty expressed or implied. See the file ‘LICENSE.GPL-3’ for details,
# or view it online at <URL:https://www.gnu.org/licenses/gpl-3.0.html>.


# Local variables:
# coding: utf-8
# mode: python
# End:
# vim: fileencoding=utf-8 filetype=python :
